#!/usr/bin/env ruby

# Author: Greg Mefford
# The purpose of this script is to:
#   Take in an image
#   Resize as necessary
#   Split it into sim-sized chunks
#   Output the sim files as follows:
#   Numbered by row and then by column
#    NW      N     NE
#       01  02  03
#     W 04  05  06  E
#       07  08  09
#    SW      S     SE

# Parameters
InputImageFilename = "Raw Terrain.png"
Rows = 2
Columns = 2
OutputFolder = "output"

# These probably won't need to be changed in most cases
SimWidth = 256
SimHeight = 256
SculptWidth = 32
SculptHeight = 32

#=== Code (No modifications should be required below this point) ===
require 'RMagick'
include Magick

def buildRAW(height_map, output_filename, raw_scalar=127)
  channel_count = 13
  if raw_scalar < 0 or raw_scalar > 255
    puts "raw_scalar must be an integer from 0 to 255"
    exit 1
  end
  
  image_width = height_map.columns
  image_height = height_map.rows

  # All channels default to black
  channels = ImageList.new
  (1..channel_count).each do |index|
    channels << Magick::Image.new(image_width, image_height) {
      self.background_color = 'black' 
    }
  end

  # Channel Descriptions
  # 1(red)       	0-255   	Height Field
  # 2(green)     	0-255   	Height Multiply Factor
  # 3(blue) 	    0-255   	Water Height of sim in Meters
  # 4 	          0-255   	Land Parcels
  # 5 (alpha 1) 	0 or 255 	For Sale land
  # 6 (alpha 2) 	0 or 255 	Public Edit Object
  # 7 (alpha 3) 	0 or 255 	Public Edit Land
  # 8 (alpha 4) 	0 or 255 	Safe Land
  # 9 (alpha 5) 	0 or 255 	Flying Allowed
  # 10 (alpha 6)  0 or 255 	Create Landmark
  # 11 (alpha 7)  0 or 255 	Outside Scripts Allowed
  # 12 (alpha 8)  0-255   	Original Heightfield
  # 13 (alpha 9)  0-255   	Original Height Multiply Factor
  
  # Height Map
  channels[0] = height_map

  # Height Scalar
  scalar_image = Magick::Image.new(image_width, image_height) {
    self.background_color = Pixel.new(raw_scalar, raw_scalar, raw_scalar) 
  }
  channels[1] = scalar_image
  # scale = (Raw_Scalar + 1).to_f / 128.0

  # Water Level in Meters
  water_level = 20
  water_image = Magick::Image.new(image_width, image_height) {
    self.background_color = Pixel.new(water_level, water_level, water_level) 
  }
  channels[2] = water_image
  
  # Write out the RAW file
  output = File.open(output_filename,"wb")
  (0..image_height-1).each do |y|
    (0..image_width-1).each do |x|
      channels.each do |channel|
        pixel = channel.pixel_color(x, y)
        printf(output, "%c", pixel.intensity)
      end
    end
  end
  output.close
end

def buildSculpt(height_map, output_filename, post_scale=2.0)
  image_width = height_map.columns
  image_height = height_map.rows

  sculpt = Magick::Image.new(SculptWidth, SculptHeight) {
    self.background_color = 'black'
  }

  # Write into the sculpt map
  (0..image_height-1).each do |y|
    (0..image_width-1).each do |x|
      # Red maps to the X axis
      red = ((256.0 / SculptWidth.to_f) * x).round
      # Green maps to the Y axis
      green = ((256.0 / SculptHeight.to_f) * y).round
      # Blue maps to the Z axis
      if (x < 1 or x >= image_width - 1 or y < 1 or y >= image_height - 1)
        # Out-of-bounds pixels have zero height
        # Make a 1-pixel zero-height border around the sculpt
        blue = 0
      else
        # In-bounds pixels have height equal to the intensity of the height map
        blue = height_map.pixel_color(x, y).intensity
      end
      # Make sure the pixel is in range
      if (x >= 0 and x < image_width and y >= 0 and y < image_height)
        sculpt.pixel_color(x, y, Pixel.new(red, green, blue))
      end
    end
  end
  # Do post-scaling
  sculpt.scale!(post_scale)
  # Flip to undo "mirror"
  sculpt.flip!
  # Write the file
  sculpt.write(output_filename)
end

# Read the input file
puts "Reading in the input file..."
input_image = Image.read(InputImageFilename).first

# Warn if the aspect ratio is wrong for the number of sims in the estate
puts "Checking the aspect ratio..."
aspect_ratio = Columns.to_f / Rows.to_f
input_aspect_ratio = input_image.columns.to_f / input_image.rows.to_f
puts "Desired aspect ratio: #{aspect_ratio}"
puts "Provided aspect ratio: #{input_aspect_ratio}"
if aspect_ratio != input_aspect_ratio
  percent_error = (100 * input_aspect_ratio - aspect_ratio).to_f / aspect_ratio
  puts "Aspect ratio was wrong by %0.2f%%. Some stretching may occur." % percent_error
end

# Resize the image to the size of the desired sims
puts "Resizing the Height Map..."
input_image.resize!(Columns * SimWidth, Rows * SimHeight)
input_image.write("#{OutputFolder}/resized_estate.png")

# Crop out each slice from the whole image
puts "Slicing out sims..."
(0..Rows-1).each do |row|
  top = row * SimHeight
  (0..Columns-1).each do |col|
    left = col * SimWidth
    image_index = row * Columns + col + 1;
    puts "Slicing out Left: #{left} Top: #{top} Width: #{SimWidth} Height: #{SimHeight}"
    slice = input_image.crop(left, top, SimWidth, SimHeight)
    slice.write("#{OutputFolder}/HeightMap %02d.png" % image_index)
    buildRAW(slice, "#{OutputFolder}/Sim %02d.raw" % image_index, 32)
    puts "Finished with Sim %02d of %02d" % [image_index, Rows * Columns]
  end
end

# Build Terrain Sculpties
puts "Slicing out terrain sculpties..."
# The height map must be flipped in order to transform the coordinates appropriately
input_image.flip!
SculptRows = (input_image.rows / (SculptHeight - 2)) + 1
SculptCols = (input_image.columns / (SculptWidth - 2)) + 1
(0..SculptRows-1).each do |row|
  # The -4 is for overlap between the sculpt
  top = row * (SculptHeight - 4)
  (0..SculptCols-1).each do |col|
    left = col * (SculptWidth - 4)
    x = left.to_f + (SculptWidth.to_f / 2.0)
    y = top.to_f + (SculptHeight.to_f / 2.0)
    slice = input_image.crop(left, top, SculptWidth, SculptHeight)
    puts "Building Terrain Sculptie 'Sculpt %0.2f, %0.2f.tga'" % [x, y]
    buildSculpt(slice, "#{OutputFolder}/Sculpt %0.2f, %0.2f.tga" % [x, y])
  end
end

puts "All done!"